﻿using Bit.Art.Api.Models;
using Bit.Art.Core;
using Bit.Art.Core.Extensions;
using Bit.Art.Core.Models;
using Bit.Art.Distributed;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NetMQ;
using NetMQ.Sockets;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Bit.Art.Api.Controllers
{
    /// <summary>
    /// 第三方对接的API
    /// </summary>
    [Authorize(Policy = "partner")]
    public class ThirdPartyController
    {
        private readonly SsoOption _option;
        private ILogger<ThirdPartyController> _logger;
        private Memcache _memcache;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="optionsAccessor"></param>
        /// <param name="cache"></param>
        /// <param name="logger"></param>
        public ThirdPartyController(IOptions<ServerOptions> optionsAccessor, Memcache cache, ILogger<ThirdPartyController> logger) 
        {
            var options = optionsAccessor.Value;
            _option = options.SsoOption;
            _memcache = cache;
            _logger = logger;
        }

        /// <summary>
        /// 批量创建用户
        /// </summary>
        /// <returns>每个用户的创建情况</returns>
        /// <param name="requests">用户信息</param>
        [HttpPost]
        public async Task<ApiCallResult<CreateAccountResponse[]>> CreateUsers([FromBody] CreateAccountRequest[] requests)
        {
            var result = new ApiCallResult<CreateAccountResponse[]>() { Success = true };
            try
            {
                using (var client = new RequestSocket(Constants.SsoApiServerEndpoint))
                {
                    var responses = new List<CreateAccountResponse>();
                    foreach (var request in requests)
                    {
                        var response = new CreateAccountResponse();
                        responses.Add(response);
                        if (string.IsNullOrEmpty(request.Username))
                        {
                            response.Success = false;
                            response.ErrorCode = Constants.ErrorCodes.EMPTY_FIELD_VALUE;
                            response.ErrorMessage = "Username";
                            continue;
                        }
                        response.Username = request.Username;
                        if (string.IsNullOrEmpty(request.Password))
                        {
                            response.Success = false;
                            response.ErrorCode = Constants.ErrorCodes.EMPTY_FIELD_VALUE;
                            response.ErrorMessage = "Password";
                            continue;
                        }
                        /* 
                         * 有的合作方如菜信的用户一开始没有手机号
                        if (string.IsNullOrEmpty(request.Cellphone))
                        {
                            response.Success = false;
                            response.ErrorCode = Constants.ErrorCodes.EMPTY_FIELD_VALUE;
                            response.ErrorMessage = "Cellphone";
                            continue;
                        }
                        */
                        request.Username = request.Username.Trim();
                        request.Password = request.Password.Trim();

                        if (request.Username.Length == 0 || request.Username.Length > 255)
                        {
                            response.Success = false;
                            response.ErrorCode = Constants.ErrorCodes.FIELD_VALUE_OUT_OF_RANGE;
                            response.ErrorMessage = "Username";
                            continue;
                        }

                        if (request.Password.Length == 0 || request.Password.Length > 32)
                        {
                            response.Success = false;
                            response.ErrorCode = Constants.ErrorCodes.FIELD_VALUE_OUT_OF_RANGE;
                            response.ErrorMessage = "Password";
                            continue;
                        }

                        /* 
                         * 有的合作方如菜信的用户一开始没有手机号
                        if (request.Cellphone.Length == 0 || request.Cellphone.Length > 20)
                        {
                            response.Success = false;
                            response.ErrorCode = Constants.ErrorCodes.FIELD_VALUE_OUT_OF_RANGE;
                            response.ErrorMessage = "Cellphone";
                            continue;
                        }
                        */

                        if (!string.IsNullOrEmpty(request.Cellphone))
                            request.Cellphone = request.Cellphone.Trim();
                        if (!string.IsNullOrWhiteSpace(request.Avatar))
                            request.Avatar = request.Avatar.Trim();
                        if (!string.IsNullOrWhiteSpace(request.Nickname))
                            request.Nickname = request.Nickname.Trim();
                        if (!string.IsNullOrEmpty(request.Avatar) && request.Avatar.Length > 255)
                        {
                            response.Success = false;
                            response.ErrorCode = Constants.ErrorCodes.FIELD_VALUE_OUT_OF_RANGE;
                            response.ErrorMessage = "Avatar";
                            continue;
                        }
                        if (!string.IsNullOrEmpty(request.Nickname) && request.Nickname.Length > 255)
                        {
                            response.Success = false;
                            response.ErrorCode = Constants.ErrorCodes.FIELD_VALUE_OUT_OF_RANGE;
                            response.ErrorMessage = "Nickname";
                            continue;
                        }

                        var msg = new AccountMessage()
                        {
                            Type = (int)AccountMessageType.Register,
                            Username = request.Username,
                            Password = request.Password,
                            Cellphone = request.Cellphone,
                            Avatar = request.Avatar,
                            Nickname = request.Nickname
                        };

                        client.SendFrame(msg.ToJson());
                        var rpcret = await Task.Run(() =>
                        {
                            var res = client.ReceiveMultipartMessage();
                            var json = res.Last.ConvertToString(System.Text.Encoding.UTF8);
                            return Utility.From<ApiCallResult<ulong>>(json);
                        });

                        response.UserId = rpcret.Result;
                        response.Success = rpcret.Success;

                        if (!response.Success)
                        {
                            response.ErrorMessage = rpcret.Message;
                            response.ErrorCode = Constants.ErrorCodes.CREARE_NEW_USER_FAILED;
                        }

                        result.Success = result.Success && response.Success;
                    }

                    result.Result = responses.ToArray();
                }
            }
            catch (Exception e)
            {
                result.Success = false; 
                result.Message = e.Message;
                result.ErrorCode = Constants.ErrorCodes.SERVER_ERROR;
                Log.Logger.Error(e, "注册新用户发生错误");
            }

            return result;
        }

        /// <summary>
        /// 用来合作方获取Access Toekn
        /// </summary>
        /// <returns>access token.</returns>
        /// <param name="request">获取的请求</param>
        [HttpPost]
        [AllowAnonymous]
        public async Task<ApiCallResult<string>> GetAccessToken([FromBody] GetAccessTokenRequest request)
        {
            // _logger.LogError("验证日志处理！");
            var response = new ApiCallResult<string>();
            try
            {
                do
                {
                    if (request == null)
                    {
                        response.Message = "request";
                        response.ErrorCode = Constants.ErrorCodes.ARGUMENT_ERROR;
                    }
                    if (string.IsNullOrEmpty(request.AppId))
                    {
                        response.Message = "需要指明AppId";
                        response.ErrorCode = Constants.ErrorCodes.ARGUMENT_ERROR;
                    }
                    if (string.IsNullOrEmpty(request.AppSecret))
                    {
                        response.Message = "需要指明AppSecret";
                        response.ErrorCode = Constants.ErrorCodes.ARGUMENT_ERROR;
                    }
                    if (string.IsNullOrEmpty(request.Scope))
                    {
                        response.Message = "需要指明Scope";
                        response.ErrorCode = Constants.ErrorCodes.ARGUMENT_ERROR;
                    }

                    if (response.ErrorCode != 0) break;

                    var tokenClient = new TokenClient(string.Format($"{_option.ServerUrl}/connect/token"), request.AppId, request.AppSecret);
                    var tokenResponse = await tokenClient.RequestClientCredentialsAsync(request.Scope);

                    if (tokenResponse.IsError)
                    {
                        response.Success = false;
                        response.Message = tokenResponse.Error;
                    }
                    else
                    {
                        response.Success = true;
                        response.Result = tokenResponse.AccessToken;
                    }
                } while (false);
            }
            catch (Exception e)
            {
                response.Success = false;
                response.Message = e.Message;
                response.ErrorCode = Constants.ErrorCodes.SERVER_ERROR;
                Log.Logger.Error(e, "获取合作方的token发生错误！");
            }

            return response;
        }

        /// <summary>
        /// 用来作为合作方代替用户获取Access Token
        /// </summary>
        /// <returns>终端用户的access token.</returns>
        /// <param name="request">获取Access Token的请求。</param>
        [HttpPost]
        public async Task<ApiCallResult<string>> GetEndUserAccessToken([FromBody] GetEndUserAccessTokenRequest request)
        {
            if (request == null) throw new ArgumentNullException(nameof(request));
            if (string.IsNullOrEmpty(request.Username)) throw new ArgumentNullException("需要指明Username");
            if (string.IsNullOrEmpty(request.Password)) throw new ArgumentNullException("需要指明Password");
            if (string.IsNullOrEmpty(request.Scope)) throw new ArgumentNullException("需要指明Scope");

            var tokenClient = new TokenClient(string.Format($"{_option.ServerUrl}/connect/token"), _option.SsoClientId, _option.SsoClientSecret);
            var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(request.Username, request.Password, request.Scope);

            var response = new ApiCallResult<string>();
            if (tokenResponse.IsError)
            {
                response.Success = false;
                response.Message = tokenResponse.Error;
            }
            else
            {
                response.Success = true;
                response.Result = tokenResponse.AccessToken;
            }

            return response;
        }

        /// <summary>
        /// 用于第三方通知存币到账，以便撮合系统更新多个用户余额信息
        /// </summary>
        /// <param name="appid">第三方的appid</param>
        /// <param name="requests">存币到账通知详情</param>
        /// <returns>用户更新后的余额</returns>
        [HttpPost]
        public async Task<ApiCallResult<UserBalanceInfo[]>> Deposit(string appid, [FromBody] DepositRequest[] requests)
        {
            var result = new ApiCallResult<UserBalanceInfo[]>();
            if (requests == null) result.Message = "request为空！";
            if (requests.Length == 0 || requests.Length > 10000) result.Message = "仅能批量更新1到10000个用户的余额！";
            if (!string.IsNullOrEmpty(result.Message))
                return result;

            var permissions = await _memcache.GetThirdPartyPermissions();
            if (permissions == null || permissions.Length == 0)
            {
                result.Success = false;
                result.Message = "当前合作方没有任何更新币种余额的权限！";
                return result;
            }
            var permittedCoins = permissions.Where(p => string.Compare(p.AppId, appid, true) == 0)
                                            .Select(p => p.Coin).ToArray();
            
            try
            {
                using (var client = new RequestSocket(SiteConfiguration.DatabaseEndpoint))
                {
                    var ubis = new List<UserBalanceInfo>();
                    foreach (var request in requests)
                    {
                        var ubi = new UserBalanceInfo()
                        {
                            Coin = request.Coin,
                            RequestId = request.Id,
                            UserId = request.UserId
                        };

                        if (string.IsNullOrEmpty(request.Coin)) ubi.ErrorMessage = "币种名为空！";
                        request.Coin = request.Coin.Trim().ToUpper();
                        if (string.IsNullOrEmpty(request.Coin)) ubi.ErrorMessage = "币种名为空！";
                        if (request.UserId == 0) ubi.ErrorMessage = "用户Id为空！";

                        if (!permittedCoins.Any(c => string.Compare(c, request.Coin, true) == 0))
                            ubi.ErrorMessage = string.Format($"无权更新币种({request.Coin})");

                        if (ubis.Any(b => string.Compare(b.Coin, request.Coin, true) == 0 && (b.UserId == request.UserId)))
                            ubi.ErrorMessage = string.Format($"用户{request.UserId}的币种{request.Coin}已经在本次更新中更新过了！");

                        ubis.Add(ubi);
                        if (!string.IsNullOrEmpty(ubi.ErrorMessage))
                            continue;

                        var ubm = new UpdateBalanceMessage()
                        {
                            UserId = request.UserId,
                            Amount = request.Amount,
                            Coin = request.Coin
                        };

                        client.SendFrame(ubm.ToJson());
                        var cached = await _memcache.GetAllBalances(request.UserId);
                        var ret = await Task.Run(() =>
                        {
                            var res = client.ReceiveMultipartMessage();
                            var json = res.Last.ConvertToString(System.Text.Encoding.UTF8);
                            return Utility.From<ApiCallResult>(json);
                        });

                        if (!ret.Success)
                        {
                            ubi.ErrorMessage = ret.Message;
                            continue;
                        }

                        var balance = cached.SingleOrDefault(b => string.Compare(b.Currency, request.Coin, true) == 0);
                        if (balance != null)
                        {
                            balance.Balance += ubm.Amount;
                        }
                        else
                        {
                            balance = new AccountBalance()
                            {
                                AccountId = ubm.UserId,
                                Balance = ubm.Amount,
                                Currency = ubm.Coin
                            };
                        }

                        ubi.Balance = balance.Balance;
                        ubi.Available = balance.Available;
                        ubi.Pending = balance.Pending;
                        ret.Success = ret.Success && await _memcache.UpdateOrAddBalance(ubm.UserId, new AccountBalance[] { balance });
                        if (!ret.Success)
                        {
                            ubi.ErrorMessage = string.Format($"无法更新用户{ubi.UserId}的余额");
                        }

                        ubi.Success = ret.Success;
                    }

                    result.Result = ubis.ToArray();
                    result.Success = ubis.All(u => u.Success);
                }
            }
            catch (Exception e)
            {
                result.Success = false;
                result.Message = e.Message;
                result.ErrorCode = Constants.ErrorCodes.SERVER_ERROR;
                Log.Logger.Error(e, "批量存储用户余额发生错误！");
            }

            return result;
        }
    }
}
